﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Web.Hosting;

using SBPweb.RIA.Server.Common.Hosting;

namespace SBPweb.RIA.Server.Common.Communication
{

    public class CustomHost : ServiceHost
    {

        #region Private fields

        private Type tService;
        private Type tContract;

        #endregion

        #region Protected fields

        protected string serviceRelativePath;
        protected IEnumerable<SiteBinding> bindings;
        //protected Dictionary<string, Uri> dictUrisByScheme = new Dictionary<string, Uri>();
        //protected Dictionary<string, Binding> dictBindings = new Dictionary<string, Binding>();

        #endregion

        #region Constructor logic

        public CustomHost(Type serviceType, Type contractType, params Uri[] baseUris)
            : this(serviceType, contractType, baseUris.FirstOrDefault().GetLeftPart(UriPartial.Path))
        {
        }

        public CustomHost(Type serviceType, Type contractType, string pathOfSvc)
            : base(serviceType, GetServiceAbsoluteUris(pathOfSvc).ToArray())
        {
            serviceRelativePath = pathOfSvc;
            bindings = SiteManager.GetBindings();

            tService = serviceType;
            tContract = contractType;
            //if (baseAddresses != null)
            //{
            //    FixUpAddresses();
            //    FixUpBaseAddresses();
            //}
            //FixUpBaseAddresses();
            MetadataBehavior = new ServiceMetadataBehavior();
            MetadataBehavior.HttpGetEnabled = true;
            Description.Behaviors.Add(MetadataBehavior);
        }

        #endregion

        #region Property accessors

        //public static int HttpPort
        //{
        //    get
        //    {
        //        return Convert.ToInt32(ConfigurationSettings.AppSettings["serviceHostHttpPort"]);
        //    }
        //}

        //public static int TcpPort
        //{
        //    get
        //    {
        //        return Convert.ToInt32(ConfigurationSettings.AppSettings["serviceHostTcpPort"]);
        //    }
        //}

        protected Type ServiceType
        {
            get
            {
                return tService;
            }
        }

        protected Type ContractType
        {
            get
            {
                return tContract;
            }
        }

        public bool IsCustomFaultBehaviorActive
        {
            get;
            set;
        }

        public bool IsServiceDebugBehaviorActive
        {
            get;
            set;
        }

        public ServiceMetadataBehavior MetadataBehavior
        {
            get;
            private set;
        }

        #endregion

        #region Methods

        #region Overridden

        protected override void InitializeRuntime()
        {
            CreateBindings();
            CreateExtensions();
            FixUpBehaviors();
            base.InitializeRuntime();
        }

        protected virtual void CreateBindings()
        {
            foreach (SiteBinding bind in bindings)
            {
                Uri uri = GetServiceAbsoluteUri(bind, serviceRelativePath);
                Binding binding = CreateBindingInstance(ContractType, bind, uri);
                ServiceEndpoint sep = AddServiceEndpoint(ContractType, binding, uri);
            }

            //Binding httpBinding = CreateBindingInstanceForHttp(ContractType, dictUrisByScheme["http"]);
            //if (httpBinding != null)
            //{
            //    this.AddServiceEndpoint(
            //        ContractType,
            //        httpBinding,
            //        dictUrisByScheme["http"]);
            //}
            //dictBindings.Add("http", httpBinding);

            //Binding tcpBinding = CreateBindingInstanceForTcp(ContractType, dictUrisByScheme["net.tcp"]);
            //if (tcpBinding != null)
            //{
            //    this.AddServiceEndpoint(
            //        ContractType,
            //        tcpBinding,
            //        dictUrisByScheme["net.tcp"]);
            //}
            //dictBindings.Add("tcp", tcpBinding);

            //Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
            //if (dictUrisByScheme.ContainsKey("net.tcp"))
            //{
            //    this.AddServiceEndpoint(
            //        typeof(IMetadataExchange),
            //        mexBinding,
            //        string.Format("{0}/mex", dictUrisByScheme["net.tcp"]));
            //}
            //dictBindings.Add("tcp.mex", mexBinding);
        }

        //protected virtual Binding CreateBindingInstanceForHttp(Type type, Uri address)
        //{
        //    return new WSDualHttpBinding();
        //}

        //protected virtual Binding CreateBindingInstanceForTcp(Type type, Uri address)
        //{
        //    return new NetTcpBinding();
        //}

        protected virtual Binding CreateBindingInstance(Type type, SiteBinding binding, Uri address)
        {
            switch (binding.Protocol)
            {
                default:
                    return new BasicHttpBinding();
                case "http":
                    return new WSDualHttpBinding();
                case "https":
                    return new WSDualHttpBinding(WSDualHttpSecurityMode.Message);
                case "net.tcp":
                    return new NetTcpBinding();
            }
        }

        protected virtual void CreateExtensions()
        {
            ;
        }

        #endregion

        #region Helper functions

        public static IEnumerable<Uri> GetServiceAbsoluteUris(string relativePath)
        {
            List<Uri> uris = new List<Uri>();
            foreach (SiteBinding binding in SiteManager.GetBindings())
            {
                uris.Add(GetServiceAbsoluteUri(binding, relativePath));
            }
            return uris;
        }

        public static Uri GetServiceAbsoluteUri(SiteBinding binding, string relativePath)
        {
            relativePath = relativePath.Replace("~/", HostingEnvironment.ApplicationVirtualPath + "/");
            return new Uri(binding.ToUri(), relativePath);
        }

        //private void FixUpAddresses()
        //{
        //    foreach (Uri uri in BaseAddresses)
        //    {
        //        if (!dictUrisByScheme.ContainsKey(uri.Scheme))
        //        {
        //            dictUrisByScheme.Add(uri.Scheme, uri);
        //        }
        //    }
        //    FixUpAddress("http", "http", HttpPort);
        //    FixUpAddress("http", "net.tcp", TcpPort);
        //}

        //private void FixUpBaseAddresses()
        //{
        //    InitializeDescription(GetServiceAbsoluteUris(serviceRelativePath));
        //}

        //private void FixUpAddress(string oldScheme, string newScheme, int newPort)
        //{
        //    if (!dictUrisByScheme.ContainsKey(newScheme) && dictUrisByScheme.ContainsKey(oldScheme))
        //    {
        //        Uri oldUri = dictUrisByScheme[oldScheme];
        //        Uri newUri = new Uri(string.Format("{0}://{1}:{2}{3}", newScheme, oldUri.Host, newPort, oldUri.PathAndQuery));
        //        dictUrisByScheme.Add(newScheme, newUri);
        //    }
        //}

        private void FixUpBehaviors()
        {
            if (IsServiceDebugBehaviorActive)
            {
                FixUpServiceDebugBehavior();
            }
            if (IsCustomFaultBehaviorActive)
            {
                FixUpCustomFaultBehavior();
            }
        }

        private void FixUpServiceDebugBehavior()
        {
            Type type = typeof(ServiceDebugBehavior);
            ServiceDebugBehavior sdb = null;
            if (Description.Behaviors.Contains(type))
            {
                sdb = Description.Behaviors[type] as ServiceDebugBehavior;
            }
            else
            {
                sdb = new ServiceDebugBehavior();
                Description.Behaviors.Add(sdb);
            }
            sdb.IncludeExceptionDetailInFaults = true;
        }

        private void FixUpCustomFaultBehavior()
        {
            Type type = typeof(CustomFaultBehavior);
            foreach (ServiceEndpoint ep in this.Description.Endpoints)
            {
                if (!ep.Behaviors.Contains(type))
                {
                    ep.Behaviors.Add(new CustomFaultBehavior());
                }
            }
        }

        #endregion

        #endregion

    }

}